/* eslint-disable*/
// import { extendArray } from './array';
// import { extendString } from './string';
// import { extendMath } from './math';

//declare global {
type msgData = Array<any>;
type msgCallbackSend = (rt: boolean, data: any) => void;
type msgCallback = (rt: boolean, msg: any) => void;
//}

type SimpleObj = Record<string, unknown>;

//const util = require('util');

extendArray();
extendString();
extendMath();

class Utils {
	static init(): void {
		// do nothong
	}

	static uuid(): string {
		const s = [];
		const hexDigits = '0123456789abcdef';
		for (let i = 0; i < 36; i++) {
			s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
		}
		s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010
		// bits 6-7 of the clock_seq_hi_and_reserved to 01
		// s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
		// eslint-disable-next-line no-multi-assign
		s[8] = s[13] = s[18] = s[23] = '-';

		return s.join('');
	}

	// https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
	static uuidv4(): string {
		return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
			const r = (Math.random() * 16) | 0;
			const v = c === 'x' ? r : (r & 0x3) | 0x8;
			return v.toString(16);
		});
	}

	static randomCode(num: number): string {
		let s = '';
		for (let i = 0; i < num; i++) {
			s += Math.randomInt(0, 10);
		}
		return s;
	}

	static isNumeric(data: string): boolean {
		// eslint-disable-next-line no-restricted-globals
		if (data === ''){
			return false;
		}
		return !isNaN(Number(data));
	}

	static try2Int(data: string): any {
		if (!this.isNumeric(data)) return data;
		return parseInt(data, 10);
	}

	static try2Number(data: string): any {
		if (!this.isNumeric(data)) return data;
		return parseFloat(data);
	}

	static try2NumberFirstnot0(data: string): any {
		if (data[0] === '0') {
			if (data.length === 1)
				return 0;
			if (data[1] !== '.'){
				return data;
			};
		}
		if (!this.isNumeric(data)) return data;
		return parseFloat(data);
	}

	// _.clone
	static clone(obj: unknown): any {
		let copy: any;

		// Handle the 3 simple types, and null or undefined
		if (obj == null || typeof obj !== 'object') return obj;

		// Handle Date
		if (obj instanceof Date) {
			return new Date(obj.getTime());
		}

		// Handle Array
		if (obj instanceof Array) {
			return obj.map((a: any) => this.clone(a));
		}

		// Handle Object
		if (obj instanceof Object) {
			copy = {};
			for (const attr of Object.keys(obj)) {
				copy[attr] = this.clone(obj[attr]);
			}
			return copy;
		}

		throw new Error("Unable to copy obj! Its type isn't supported.");
	}

	static deepCopy(a: unknown): any {
		return JSON.parse(JSON.stringify(a));
	}

	static shallowCopy(src: unknown, des: unknown): void {
		if (src === des) {
			return;
		}
		if (Array.isArray(src) && Array.isArray(des)) {
			for (let i = 0; i < src.length; i++) {
				des[i] = src[i];
			}
		} else if (typeof src === 'object' && typeof des === 'object') {
			for (const [k, v] of Object.entries(src)) {
				des[k] = v;
			}
		}
	}

	static logFull(msg: unknown): void {
		//const util = require('util');
		//console.log(util.inspect(msg, false, null, true /* enable colors */));
	}

	// _.isEmpty
	static isObjectEmpty(obj: unknown): boolean {
		return !obj || Object.keys(obj).length === 0;
	}

	// _.size
	static lenObject(obj: unknown): number {
		return Object.keys(obj).length;
	}

	// like format("{0},{1}!",'hello,world') => hello,world!
	static format(msg: string, arg: string): string {
		return msg.format(arg);
	}

	static objectsEqualSimple(a: {}, b: {}): boolean {
		if (a === b) return true;
		if (a == null || b == null) return false;
		if (typeof a !== 'object' || typeof b !== 'object') {
			return false;
		}
		if (this.lenObject(a) !== this.lenObject(b)) {
			return false;
		}
		for (let key of Object.keys(a)) {
			if (a[key] !== b[key]) return false;
		}
		return true;
	}

	static arraysEqualSimple(a: any[], b: any[]): boolean {
		if (a === b) return true;
		if (a == null || b == null) return false;
		if (a.length !== b.length) return false;

		// If you don't care about the order of the elements inside
		// the array, you should sort both arrays here.
		// Please note that calling sort on an array will modify that array.
		// you might want to clone your array first.

		for (let i = 0; i < a.length; ++i) {
			if (a[i] !== b[i]) return false;
		}
		return true;
	}

	//not handle error!
	static getFullInfo(info: SimpleObj, fullkey: string): unknown {
		let keys = fullkey.split('.');
		for (let i = 0; i < keys.length; i++) {
			let key = keys[i];
			info = info[key] as SimpleObj;
			if (!info) {
				return null;
			}
		}
		return info;
	}

	//not handle error!
	static updateInfo(info: object, fullkey: string, value: unknown): void {
		let keys = fullkey.split('.');
		for (let i = 0; i < keys.length - 1; i++) {
			let key = keys[i];
			if (!info[key]) {
				let nkey = Number(keys[i + 1]);
				info[key] = isNaN(nkey) ? {} : [];
			}
			info = info[key];
		}
		info[keys[keys.length - 1]] = value;
		// let key = fullkey;
		// // console.log("MSG_AOI_UPDATE",this.name,key,value)
		// while (1) {
		// 	const find = key.indexOf('.');
		// 	if (find < 0) {
		// 		info[key] = value;
		// 		break;
		// 	}
		// 	const left = key.substring(0, find);
		// 	if (!info[left]) {
		// 		info[left] = {};
		// 	}
		// 	info = info[left];
		// 	key = key.substring(find + 1);
		// }
	}

	static objectLen(obj: Object): number {
		return Object.keys(obj).length;
	}

	/**
	 * 根据品质设置文本颜色
	 *
	 * 1.绿色，2.蓝色，3紫色，4橙色，5红色
	 * @param rank
	 */
	static getColorByRank(rank: number): number {
		let color: number;
		//
		switch (rank) {
			case 1:
				color = 0x01c52e;
				break;
			case 2:
				color = 0x27b4ff;
				break;
			case 3:
				color = 0xcb3cff;
				break;
			case 4:
				color = 0xfd4071;
				break;
			case 5:
				color = 0xef7a3e;
				break;
			case 6:
				color = 0xda3232;
				break;
			case 7:
				color = 0xda3232;
				break;
			case 8:
				color = 0xda3232;
				break;
			case 9:
				color = 0xda3232;
				break;
			default:
		}
		return color;
	}

	static getMsgColorBy(rank: number): string {
		let grb: string;
		switch (rank) {
			case 1:
				grb = '#C';
				break;
			case 2:
				grb = '#G';
				break;
			case 3:
				grb = '#B';
				break;
			case 4:
				grb = '#Z';
				break;
			case 5:
				grb = '#P';
				break;
			case 6:
				grb = '#J';
				break;
			case 7:
				grb = '#R';
				break;
			case 8:
				grb = '#R';
				break;
			case 9:
				grb = '#R';
				break;
			default:
		}
		return grb;
	}

	static formatString(v: string, vars: SimpleObj): string {
		return this.formatDataV(v, vars) || v;
	}

	static formatDataV(v: string, vars: SimpleObj): string {
		let m = v.match(/\$\{\w+\}/);
		if (!m) {
			return null;
		}
		let key = m[0].substr(2, m[0].length - 3);
		if (m[0].length === v.length) {
			return vars[key] as string;
		}

		while (m) {
			key = m[0].substr(2, m[0].length - 3);
			const data = vars[key] as string;
			if (data != null) {
				v = v.replaceRawAll(m[0], data);
			} else {
				console.warn(`formatDataV ${v} key ${key} not find`);
				break;
			}
			m = v.match(/\$\{\w+\}/);
		}
		return v;
	}

	static formatDataByVar(_data: SimpleObj, vars: SimpleObj): SimpleObj {
		const data = { ..._data };
		for (const [key, v] of Object.entries(_data)) {
			if (typeof v === 'string') {
				const rt = Utils.formatDataV(v, vars);
				if (rt != null) {
					data[key] = rt;
				}
			}
		}
		return data;
	}

	static className(c: Object): string {
		return c.constructor.name;
	}

	static clamp(v: number, min: number, max: number): number {
		if (min != null && v < min) {
			return min;
		}
		if (max != null && v > max) {
			return max;
		}
		return v;
	}	

} // class Utils
